home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / prog_c / cppnl009.zip / CPPNL.009 next >
Text File  |  1996-04-17  |  15KB  |  446 lines

  1. Issue #009
  2. April, 1996
  3.  
  4.  
  5. Contents:
  6.  
  7. Introduction to Templates Part 1 - Function Templates
  8. New C++ Feature - Member Templates
  9. Introduction to Stream I/O Part 4 - Tie()
  10. Using C++ as a Better C Part 9 - Extern "C"
  11. Correction
  12.  
  13.  
  14. INTRODUCTION
  15.  
  16. In this issue we'll start discussing C++ templates, and present a new
  17. aspect of them known as member templates.  There will also be a
  18. continuation of the series on stream I/O, along with a discussion of
  19. how to combine C++ code with code written in other languages.
  20.  
  21.  
  22. INTRODUCTION TO C++ TEMPLATES PART 1 - FUNCTION TEMPLATES
  23.  
  24. In issue #007 we talked about the use of inline functions.  Suppose
  25. that you wish to compute the maximum of two quantities, and you define
  26. a C macro for this:
  27.  
  28.         #define max(a, b) ((a) > (b) ? (a) : (b))
  29.  
  30. This works OK until a case like:
  31.  
  32.         max(x++, y++)
  33.  
  34. comes along.  An inline function:
  35.  
  36.         inline int max(int a, int b)
  37.         {
  38.                 return a > b ? a : b;
  39.         }
  40.  
  41. solves this problem.  But what if you want a max() function for a
  42. variety of numeric types?  You can define a slew of function
  43. prototypes:
  44.  
  45.         int max(int, int);
  46.  
  47.         long max(long, long);
  48.  
  49.         double max(double, double);
  50.  
  51. and rely on function overloading to sort things out.  Or you can
  52. define one function that might work in all cases:
  53.  
  54.         long double max(long double, long double);
  55.  
  56. since nothing can be bigger than a long double, right?  This last
  57. approach fails because there's no guarantee that, for example, the
  58. size of a long is less than the size of a long double, and assigning a
  59. long to a long double would in such a case result in loss of precision.
  60.  
  61. In C++ there is another way to approach this problem, using what are
  62. called parameterized types or templates.  We can define a function
  63. template:
  64.  
  65.         template <class T> inline T max(T a, T b)
  66.         {
  67.                 return a > b ? a : b;
  68.         }
  69.  
  70. The preface "template <class T>" is used to declare a template.  T is
  71. a template parameter, a type argument to the template.  When this
  72. template is used:
  73.  
  74.         int a = 37;
  75.         int b = 47;
  76.  
  77.         int i = max(a, b);
  78.  
  79. the type value of T will be "int", because a and b are integers.  If
  80. instead I had said:
  81.  
  82.         double a = 37.53;
  83.         double b = -47.91;
  84.  
  85.         double d = max(a, b);
  86.  
  87. then T would have the type value "double".  The process of generating
  88. an actual function from a function template is known as
  89. "instantiation".  Note also that "const T&" may be used instead of
  90. "T"; we will be discussing this point in a future issue of the
  91. newsletter.
  92.  
  93. This template will also work on non-numeric types, so long as they
  94. have the ">" operator defined.  For example:
  95.  
  96.         class A {
  97.         public:
  98.                 int operator>(const A&); // use "bool" return type
  99.                                          // instead, if available
  100.         };
  101.  
  102.         A a;
  103.         A b;
  104.  
  105.         A c = max(a, b);
  106.  
  107. Templates are a powerful but complex feature, about which we will have
  108. more to say.  Languages like C or Java, that do not have templates,
  109. typically use macros or rely on using base class pointers and virtual
  110. functions to synthesize some of the properties of templates.
  111.  
  112. Templates in C++ are a more ambitious attempt to support "generic
  113. programming" than some previous efforts found in other programming
  114. languages.  Support for generic programming in C++ is considered by
  115. some to be as important a language goal for C++ as is support for
  116. object-oriented programming (using base/derived classes and virtual
  117. functions; see newsletter issue #008).  An example of heavy template
  118. use can be found in STL, the Standard Template Library.
  119.  
  120.  
  121. NEW C++ FEATURE - MEMBER TEMPLATES
  122.  
  123. In chapter 14 of the draft ANSI/ISO C++ standard is a mention of
  124. something called member templates.  This feature is new in a way and
  125. not yet widely available, but worth mentioning here.
  126.  
  127. Member templates are simply a generalization of templates such that a
  128. template can be a class member.  For example:
  129.  
  130.         #include <stdio.h>
  131.  
  132.         template <class A, class B> struct Pair {
  133.                 A a;
  134.                 B b;
  135.  
  136.                 Pair(const A& ax, const B& bx) : a(ax), b(bx) {}
  137.  
  138.                 template <class T, class U> Pair(const Pair<T,U>& p) :
  139.                     a(p.a), b(p.b) {}
  140.         };
  141.  
  142.         int main()
  143.         {
  144.                 Pair<short, float> x(37, 12.34);
  145.                 Pair<long, long double> y(x);
  146.  
  147.                 printf("%ld %Lg\n", y.a, y.b);
  148.  
  149.                 return 0;
  150.         }
  151.  
  152. This is an adaptation of a class found in the Standard Template
  153. Library.  Note that an object of class Pair<long, long double> is
  154. constructed from an object of class Pair<short, float>.  By using a
  155. template constructor it is possible to construct a Pair from any other
  156. Pair, assuming that conversion from T to A and U to B are supported.
  157. Without the availability of template constructors one could only
  158. declare constructors with fixed types like "Pair(int)" or else use the
  159. template arguments to Pair itself, as in "Pair(A, B)".
  160.  
  161. In a similar way to function template use, it's possible to have usage
  162. like:
  163.  
  164.         template <class T> struct A {
  165.                 template <class U> struct B {/* stuff */};
  166.         };
  167.  
  168.         A<double>::B<long> ab;
  169.  
  170. In this example, the type value of T within the nested template
  171. declaration would be "double", while the value of U would be "long".
  172.  
  173. There are a few restrictions on member templates.  A destructor for a
  174. class cannot be defined as a function template, nor may a function
  175. template member of a class be virtual.
  176.  
  177.  
  178. INTRODUCTION TO STREAM I/O PART 4 - TIE()
  179.  
  180. In issue #008 we talked about copying files using a variety of
  181. methods.  One example that was presented was this one:
  182.  
  183.         #include <iostream.h>
  184.  
  185.         int main()
  186.         {
  187.                 char c;
  188.  
  189.                 cin.unsetf(ios::skipws);
  190.  
  191.                 while (cin >> c)
  192.                         cout << c;
  193.  
  194.                 return 0;
  195.         }
  196.  
  197. Jerry Schwarz suggested that it might be worth discussing the tie()
  198. function and its effect on the performance of this code.
  199. Specifically, if we slightly change the above code to:
  200.  
  201.         #include <iostream.h>
  202.  
  203.         int main()
  204.         {
  205.                 char c;
  206.  
  207.                 cin.tie(0);
  208.  
  209.                 cin.unsetf(ios::skipws);
  210.  
  211.                 while (cin >> c)
  212.                         cout << c;
  213.  
  214.                 return 0;
  215.         }
  216.  
  217. it runs about 8X faster with one popular C++ compiler, and about 18X
  218. with another.
  219.  
  220. The difference has to do with buffering and flushing of streams.  When
  221. input is requested, for example with:
  222.  
  223.         cin >> c
  224.  
  225. there may be output pending in the buffer for the output stream.  The
  226. input stream is therefore tied to the output stream such that a
  227. request for input will cause pending output to be flushed.  Flushing
  228. output is expensive, typically triggering a flush() call and a write()
  229. system call (on UNIX systems).  Disabling the linkage between the
  230. input and output streams gets rid of this overhead.
  231.  
  232. To further illustrate this point, consider another example:
  233.  
  234.         #include <iostream.h>
  235.  
  236.         int main()
  237.         {
  238.                 char buf[100];
  239.  
  240.                 //cin.tie(0);
  241.  
  242.                 cin.unsetf(ios::skipws);
  243.  
  244.                 cout.unsetf(ios::unitbuf);
  245.  
  246.                 cout << "What is your name? ";
  247.  
  248.                 cin >> buf;
  249.  
  250.                 return 0;
  251.         }
  252.  
  253. It's common for output to be completely unbuffered (unit buffering) if
  254. going to a terminal (screen or window).  So setting cin.tie(0) will not
  255. necessarily change observable behavior, because output will be flushed
  256. immediately in all cases.
  257.  
  258. To affect behavior in this example, one also needs to disable unit
  259. buffering for the stream, achieved by saying:
  260.  
  261.         cout.unsetf(ios::unitbuf);
  262.  
  263. Once this is done, cin.tie(0) will change behavior in a visible way.
  264. If the input stream is untied, then the prompt in the example above
  265. will not come out before input is requested from the user, leading to
  266. confusion.
  267.  
  268. Note also that current libraries vary in their behavior.  The above
  269. example works for one library that was tried, but for another, there
  270. appears to be no way to disable unit buffering under any
  271. circumstances, when output is to a terminal.  The draft ANSI/ISO C++
  272. standard calls for unit buffering to be set for error output ("cerr").
  273.  
  274. If tie() is called with no argument, it returns the stream currently
  275. tied to.  For example:
  276.  
  277.         cout << (void*)cin.tie() << "\n";
  278.  
  279.         cout << (void*)(&cout) << "\n";
  280.  
  281. give identical results if cin is currently tied to cout.
  282.  
  283. Copying files a character at a time has other pitfalls.  One has to be
  284. careful in assessing the buffering and function call overhead for
  285. anything done on a per-character basis.  There is yet another way of
  286. copying files by character, using streambufs, that we'll present in a
  287. future issue.
  288.  
  289.  
  290. USING C++ AS A BETTER C PART 9 - EXTERN "C"
  291.  
  292. One of the common issues that always comes up with programming
  293. languages is how to mix code written in one language with code written
  294. in another.
  295.  
  296. For example, suppose that you're writing C++ code and wish to call C
  297. functions.  A common case of this would be to access C functions that
  298. manipulate C-style strings, for example strcmp() or strlen().  So as a
  299. first try, we might say:
  300.  
  301.         extern size_t strlen(const char*);
  302.  
  303. and then use the function.  This will work, at least at compile time,
  304. but will probably give a link error about an unresolved symbol.
  305.  
  306. The reason for the link error is that a typical C++ compiler will
  307. modify the name of a function or object ("mangle" it), for example to
  308. include information about the types of the arguments.  As an example, a
  309. common scheme for mangling the function name strlen(const char*) would
  310. result in:
  311.  
  312.         strlen__FPCc
  313.  
  314. There are two purposes for this mangling.  One is to support function
  315. overloading.  For example, the following two functions cannot both be
  316. called "f" in the object file symbol table:
  317.  
  318.         int f(int);
  319.  
  320.         int f(double);
  321.  
  322. But suppose that overloading was not an issue, and in one compilation
  323. unit we have:
  324.  
  325.         extern void f(double);
  326.  
  327. and we use this function, and its name in the object file is just
  328. "f".  And suppose that in another compilation unit the definition is
  329. found, as:
  330.  
  331.         void f(char*) {}
  332.  
  333. This will silently do the wrong thing -- a double will be passed to a
  334. function requiring a char*.  Mangling the names of functions
  335. eliminates this problem, because a linker error will instead be
  336. triggered.  This technique goes by the name "type safe linkage".
  337.  
  338. So to be able to call C functions, we need to disable name mangling.
  339. The way of doing this is to say:
  340.  
  341.         extern "C" size_t strlen(const char*);
  342.  
  343. or:
  344.  
  345.         extern "C" {
  346.                 size_t strlen(const char*);
  347.                 int strcmp(const char*, const char*);
  348.         }
  349.  
  350. This usage is commonly seen in header files that are used both by C
  351. and C++ programs.  The extern "C" declarations are conditional based
  352. on whether C++ is being compiled instead of C.
  353.  
  354. Because name mangling is disabled with a declaration of this type,
  355. usage like:
  356.  
  357.         extern "C" {
  358.                 int f(int);
  359.                 int f(double);
  360.         }
  361.  
  362. is illegal (because both functions would have the name "f").
  363.  
  364. Note that extern "C" declarations do not specify the details of what
  365. must be done to allow C++ and C code to be mixed.  Name mangling is
  366. commonly part of the problem to be solved, but only part.
  367.  
  368. There are other issues with mixing languages that are beyond the scope
  369. of this presentation.  The whole area of calling conventions, such as
  370. the order of argument passing, is a tricky one.  For example, if every
  371. C++ compiler used the same mangling scheme for names, this would not
  372. necessarily result in object code that could be mixed and matched.
  373.  
  374.  
  375. CORRECTION
  376.  
  377. In issue #008 we talked about copying files and said this about one of
  378. the examples of copying files using C:
  379.  
  380.     This approach works on text files.  Unfortunately, however, for binary
  381.     files, an attempt to copy a 10406-byte file resulted in output of only
  382.     383 bytes.  Why?  Because EOF is itself a valid character that can
  383.     occur in a binary file.  If set to -1, then this is equivalent to 255
  384.     or 0377 or 0xff, a perfectly legal byte in a file.
  385.  
  386. This isn't quite the case.  A common mistake when copying files in C
  387. is to use a char instead of an int with getc() and putc().  If a char
  388. is used, then the explanation above is correct, because with a binary
  389. file EOF interpreted as a character is one of the 256 valid bit
  390. patterns that a char can hold.
  391.  
  392. But with an int this is not a problem.  getc(), and its functional
  393. equivalent fgetc(), return an unsigned char converted to an int.  So
  394. the int can represent all character values 0-255, along with the EOF
  395. marker (typically -1).
  396.  
  397. It turns out that the reason why the example failed was due to a ^Z in
  398. the file.  ^Z used to be used as an end-of-file marker for DOS files
  399. used on PCs.
  400.  
  401. Thanks to David Nelson for mentioning this.
  402.  
  403.  
  404. ACKNOWLEDGEMENTS
  405.  
  406. Thanks to Nathan Myers, Eric Nagler, David Nelson, Terry Rudd,
  407. Jonathan Schilling, and Clay Wilson for help with proofreading.
  408.  
  409.  
  410. SUBSCRIPTION INFORMATION / BACK ISSUES
  411.  
  412. To subscribe to the newsletter, send mail to majordomo@world.std.com
  413. with this line as its message body:
  414.  
  415. subscribe c_plus_plus
  416.  
  417. Back issues are available via FTP from:
  418.  
  419.         rmii.com /pub2/glenm/newslett
  420.  
  421. or on the Web at:
  422.  
  423.         http://www.rmii.com/~glenm
  424.  
  425. There is also a Java newsletter.  To subscribe to it, say:
  426.  
  427. subscribe java_letter
  428.  
  429. using the same majordomo@world.std.com address.
  430.  
  431. -------------------------
  432.  
  433. Copyright (c) 1996 Glen McCluskey.  All Rights Reserved.
  434.  
  435. This newsletter may be further distributed provided that it is copied
  436. in its entirety, including the newsletter number at the top and the
  437. copyright and contact information at the bottom.
  438.  
  439. Glen McCluskey & Associates
  440. Professional C++ Consulting
  441. Internet: glenm@glenmccl.com
  442. Phone: (800) 722-1613 or (970) 490-2462
  443. Fax: (970) 490-2463
  444. FTP: rmii.com /pub2/glenm/newslett (for back issues)
  445. Web: http://www.rmii.com/~glenm
  446.